package com.uow.algdemo.animation;

import java.util.ArrayList;
import java.util.List;

import com.uow.algdemo.ui.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;

/**
 * This class will receive data of the Hanoi Tower and animation steps, then generate animation on animation layout.
 */
public class AnimationHanoi extends RelativeLayout implements AnimationInterface
{
	/**
     * This is handler for whole progress of animation.
     */
	private Handler handler;
	/**
     * This is current status of disks moving or not.
     */
	private boolean running = false;
	/**
     * This is current status of animation completed or not.
     */
	private boolean finished = false;
	/**
     * This is current status of pause or not.
     */
	private boolean pause = false;
	/**
     * This array contains view of disks.
     */
	private ImageView[] disk;
	/**
     * This array contains image resource of background.
     */
	private ImageView background;
	/**
     * This array contains positions of disks' view.
     */
	private int[] diskPos;
    /**
     * This is the number of panels waiting for sort.
     */
	private int total = 3;
    /**
     * This array contains steps of the Hanoi Tower solution animation.
     */
	private List<int[][]> steps = new ArrayList<int[][]>();
    /**
     * This is the current step of animation position.
     */
	private int position = 0;
    /**
     * This is the current speed of animation.
     */
	private int speed = 10;
	/**
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param newTotal Number of disks.
     * @param newSteps Steps of the queens problem solution animation.
     * @param width Width of layout in pixel.
     * @param height Height of layout in pixel.
     */
	public AnimationHanoi(Context context, int newTotal, List<int[][]> newSteps, int width, int height)
	{
        super(context);
		steps = newSteps;
        total = newTotal;
        background = new ImageView(context);
        background.setImageResource(R.drawable.ic_bg_hanoi);
		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(450, 500);
		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		params.bottomMargin = ((height - 500) / 2);
		params.addRule(RelativeLayout.CENTER_HORIZONTAL);
        this.addView(background,params);

        int[] diskid = new int[8];
        diskid[0] = R.drawable.ic_hanoi_1;
        diskid[1] = R.drawable.ic_hanoi_2;
        diskid[2] = R.drawable.ic_hanoi_3;
        diskid[3] = R.drawable.ic_hanoi_4;
        diskid[4] = R.drawable.ic_hanoi_5;
        diskid[5] = R.drawable.ic_hanoi_6;
        diskid[6] = R.drawable.ic_hanoi_7;
        diskid[7] = R.drawable.ic_hanoi_8;
        diskPos = new int[total];
        disk = new ImageView[total];

        for(int j = 0; j < total; j++)
        {
        	diskPos[j] = 0;
        }

        for(int i = (total - 1); i >= 0; i--)
        {
        	disk[i] = new ImageView(context);
        	disk[i].setImageResource(diskid[i]);
    		params = new RelativeLayout.LayoutParams(120, 15);
    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    		params.bottomMargin = 35 + ((height - 500) / 2) + (total-i-1) * 20;
    		params.leftMargin = 15 + ((width - 450) / 2);
    		this.addView(disk[i],params);
        }
    }
	/**
	 * @param newSteps The steps to set
	 */
	public void setSteps(List<int[][]> newSteps)
	{
		this.steps = newSteps;
	}
	/**
	 * @return The speed
	 */
	public int getSpeed()
	{
		return speed;
	}
	/**
	 * @return The animation status
	 */
	public boolean getStatus()
	{
		return finished;
	}
    /**
     * This method set speed of animation.
     * @param newSpeed Speed value to set.
     */	
	public void setSpeed(int newSpeed)
	{
		speed = newSpeed;
	}
    /**
     * This method send feedback from this class for different command.
     * @param mes Message to current class.
     * @return an Array for feedback information.
     */
	public int[][] sendMessage(int mes)
	{
		int[][] tmp = new int[1][mes];
		return tmp;
	}
	/**
     * This method generate animation and start animation.
     */
	@SuppressLint({ "HandlerLeak", "HandlerLeak" })
	public void runAnimation()
	{
		handler = new Handler()
		{
			public void handleMessage(Message msg)
			{
				switch (msg.what)
				{
					case 1:
					{
						if(pause == false && finished == false)
						{
							if(position < steps.size())
							{
								int[][] a = steps.get(position);
						    	move(a[0][0],a[1][0],a[2][0]);
						    	position++;
							}
							else
							{
								finished = true;
							}
						}
						break;
					}
				}
				super.handleMessage(msg);
			}
		};

    	handler.sendEmptyMessage(1);
	}

    /**
     * This method pause animation.
     */
	public void pauseAnimation()
	{
		pause = true;
	}
    /**
     * This method resume animation from pause position.
     */
	public void resumeAnimation()
	{
		pause = false;
		if(!running)
		{
			handler.sendEmptyMessage(1);
		}
	}
    /**
     * This method stop animation and clear layout.
     */	
	public void stopAnimation()
	{
		finished = true;
	}
    /**
     * This method show next step of animation when animation is pause.
     */	
	public void nextStep()
	{
		if(pause == true && running == false)
		{
			if(position < steps.size())
			{
				int[][] a = steps.get(position);
		    	move(a[0][0],a[1][0],a[2][0]);
		    	position++;
			}
		}
	}
    /**
     * This method show last step of animation when animation is pause.
     */	
	public void lastStep()
	{
		if(pause == true && running == false)
		{
			if(position > 0)
			{
				position--;
				int[][] a = steps.get(position);
		    	move(a[0][0],a[2][0],a[1][0]);
			}
		}
	}
	/**
     * This method generate animation for move a panel from original stick to target stick.
     * @param diskIndex Index of disk moving.
     * @param orgStick Index of original stick.
     * @param tarStick Index of target stick.
     */
	private void move(final int diskIndex, int orgStick, int tarStick)
	{
		final int step1;
		final int step2;
		final int step3;
		final int stepSpeed = speed * 150;

		int numOrg = 0;
		int numDes = 0;

		for(int k = 0; k < total; k++)
		{
			if(diskPos[k] == orgStick)
			{
				numOrg++;
			}
		}

		for(int k = 0; k < total; k++)
		{
			if(diskPos[k] == tarStick)
			{
				numDes++;
			}
		}
		
		step1 = -260 + (numOrg * 20);
		step2 = (tarStick - orgStick) * 150;
		step3 = 260 - ((numDes + 1) * 20);
		running = true;

		TranslateAnimation animationStepOne = new TranslateAnimation(0, 0, 0, step1);
		animationStepOne.setDuration(stepSpeed);
        disk[diskIndex].startAnimation(animationStepOne);

        animationStepOne.setAnimationListener(new AnimationListener()
        {
	        public void onAnimationEnd(Animation animation)
	        {
	        	disk[diskIndex].clearAnimation();
	        	disk[diskIndex].layout(disk[diskIndex].getLeft(), disk[diskIndex].getTop() + step1, disk[diskIndex].getRight(), disk[diskIndex].getBottom() + step1);
	            TranslateAnimation animationStepTwo = new TranslateAnimation(0, step2, 0, 0);
	            animationStepTwo.setDuration(stepSpeed);
	            disk[diskIndex].startAnimation(animationStepTwo);

	            animationStepTwo.setAnimationListener(new AnimationListener()
	            {
	    	        public void onAnimationEnd(Animation animation)
	    	        {
	    	        	disk[diskIndex].clearAnimation();
	    	        	disk[diskIndex].layout(disk[diskIndex].getLeft() + step2, disk[diskIndex].getTop(), disk[diskIndex].getRight() + step2, disk[diskIndex].getBottom());

	    	        	TranslateAnimation animationStepThree = new TranslateAnimation(0, 0, 0, step3);
	    	        	animationStepThree.setDuration(stepSpeed);
	    	            disk[diskIndex].startAnimation(animationStepThree);

	    	            animationStepThree.setAnimationListener(new AnimationListener()
	    	            {
	    	    	        public void onAnimationEnd(Animation animation)
	    	    	        {
	    	    	        	disk[diskIndex].clearAnimation();
	    	    	        	disk[diskIndex].layout(disk[diskIndex].getLeft(), disk[diskIndex].getTop() + step3, disk[diskIndex].getRight(), disk[diskIndex].getBottom() + step3);
	    	    	        	running = false;
	    	    	        	handler.sendEmptyMessage(1);
	    	    	        }

	    	    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
	    	    			public void onAnimationStart(android.view.animation.Animation arg0) {}
	    	            });
	    	        }

	    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
	    			public void onAnimationStart(android.view.animation.Animation arg0) {}
	            });
	        }

			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
			public void onAnimationStart(android.view.animation.Animation arg0) {}
        });
        diskPos[diskIndex] = tarStick;
	}
}
